﻿using Newtonsoft.Json;
using Qins.PluginInterface;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using TraceLog;

namespace Qins.PluginLoader
{
    public sealed class PluginsLoader
    {
        private static readonly PluginsLoader instance = new PluginsLoader();
        /// <summary>
        /// 显式的静态构造函数用来告诉C#编译器在其内容实例化之前不要标记其类型
        /// </summary>
        static PluginsLoader() { }
        private PluginsLoader() { }
        public static PluginsLoader Instance { get { return instance; } }


        public Dictionary<string, PluginAssemblyLoadContext> plugins = new Dictionary<string, PluginAssemblyLoadContext>();
        public Dictionary<string, IPlugin> pluginComs = new Dictionary<string, IPlugin>();


        public void load(string pluginsPath)
        {
            try
            {
                if (!Directory.Exists(pluginsPath))
                    DirControl.CreateDirectory(pluginsPath);

                DirectoryInfo TheFolder = new DirectoryInfo(pluginsPath);
                //遍历文件
                foreach (FileInfo NextFile in TheFolder.GetFiles())
                {
                    if (NextFile.Extension.ToLower() == ".dll")
                    {
                        string pluginName = NextFile.Name.Replace(NextFile.Extension, "");
                        Assembly pluginAssembly = LoadPlugin(pluginName, NextFile.FullName);
                        CreatePlugins(pluginName, pluginAssembly);
                    }
                }
            }
            catch (Exception ex)
            {

                throw;
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="key"></param>
        /// <param name="relativePath"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private Assembly LoadPlugin(string key, string relativePath)
        {
            try
            {
                if (!plugins.TryGetValue(key, out PluginAssemblyLoadContext loadContext))
                {
                    loadContext = new PluginAssemblyLoadContext(relativePath);
                    plugins[key] = loadContext;
                }

                var assembly = loadContext.LoadFromAssemblyName(new AssemblyName(key));
                //Assembly assembly = Assembly.LoadFile(relativePath);
                //CreatePlugins(key, assembly);
                return assembly;
            }
            catch (Exception ex)
            {

                throw;
            }

        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private void CreatePlugins(string key, Assembly assembly)
        {
            foreach (Type type in assembly.GetTypes())
            {
                //if(typeof(IPlugin).FullName==type.FullName)
                if (typeof(IPlugin).IsAssignableFrom(type))
                {
                    if (!pluginComs.TryGetValue(key, out IPlugin loadContext))
                    {
                        var plugintype = Activator.CreateInstance(type);
                        var comss = plugintype as IPlugin;
                        if (comss != null)
                        {
                            Message message = new Message();
                            message.Send += ReceiveMessage;//绑定主程序接收方法
                            comss.Initialization(message);

                            pluginComs[plugintype.ToString()] = comss;
                        }
                        //IPlugin result = Activator.CreateInstance(type) as IPlugin;
                    }
                }
            }
        }


        public object Run(string name, object parameter)
        {
            try
            {
                if (pluginComs.TryGetValue(name, out IPlugin plugin))
                {
                    return plugin.Execute(parameter);
                }
                return null;
            }
            catch (Exception ex)
            {
                Trace.Write(new TraceLogType(WriteLogType.info, name), parameter.ToString()+"\r\n"+ex.Message);
                return ex.Message;
                //这里需要一个日志记录问题
                //throw;
            }
            return null;
        }

        public List<PluginInfo> GetAll()
        {
            List<PluginInfo> lpi = new List<PluginInfo>();
            foreach (KeyValuePair<string, IPlugin> kvp in pluginComs)
            {
                PluginInfo pgi = new PluginInfo();
                pgi.Key = kvp.Key;
                pgi.Name = kvp.Value.PluginName;
                pgi.Description = kvp.Value.Description;
                pgi.InDescription = kvp.Value.InDescription;
                pgi.OutDescription = kvp.Value.OutDescription;
                pgi.AutoRun = kvp.Value.AutoRun;


                string asmkey = kvp.Key.Substring(0, kvp.Key.LastIndexOf("."));
                if (plugins.TryGetValue(asmkey, out PluginAssemblyLoadContext loadContext))
                {
                    var assembly = loadContext.LoadFromAssemblyName(new AssemblyName(asmkey));
                    pgi.DllFullName = assembly.FullName;
                }
                lpi.Add(pgi);
            }

            return lpi;
        }

        private string ReceiveMessage(string pluginName, MessageContent message)
        {
            string ReceiveStr = "";
            switch (message.messageType)
            {
                case MessageConentTypeEnum.GetAllPlugin:
                    {
                        message.content = JsonConvert.SerializeObject(GetAll());
                        ReceiveStr = (string)Run(pluginName, message);
                        break;
                    }
                case MessageConentTypeEnum.OpenUrl:
                    {
                        var args = new PluginUpdateEventArgs(message);
                        PluginUpdated(this, args);
                        break;
                    }
                case MessageConentTypeEnum.Run:
                    {
                        ReceiveStr = (string)Run(pluginName, message.content);
                        break;
                    }
                case MessageConentTypeEnum.Log:
                    {
                        Trace.Write(new TraceLogType(WriteLogType.info, pluginName), message.content);
                        break;
                    }
                case MessageConentTypeEnum.SetWindow:
                    {
                        var args = new PluginUpdateEventArgs(message);
                        PluginUpdated(this, args);
                        break;
                    }
                case MessageConentTypeEnum.SetUserInfo:
                    {
                        var args = new PluginUpdateEventArgs(message);
                        PluginUpdated(this, args);
                        break;
                    }
                case MessageConentTypeEnum.GetVersion:
                    {
                        var args = new PluginUpdateEventArgs(message);
                        ReceiveStr = PluginUpdated(this, args);
                        break;
                    }
                default:
                    {
                        ReceiveStr = "no switch";
                        break;
                    }
            }
            return ReceiveStr;
        }

        public delegate string PluginUpdateHandler(object sender, PluginUpdateEventArgs e);

        //声明一个更新Address的事件
        public event PluginUpdateHandler PluginUpdated;

        public class PluginUpdateEventArgs : System.EventArgs
        {
            private MessageContent mContent;
            public PluginUpdateEventArgs(MessageContent sContent)
            {
                this.mContent = sContent;
            }
            public MessageContent Content { get { return mContent; } }
        }
    }
}
